/////////////////////////////////////////////////////////////////////////////
//
// (c) 2007 BinaryComponents Ltd.  All Rights Reserved.
//
// http://www.binarycomponents.com/
//
/////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections;
using System.Collections.Generic;

namespace SuperListEx.Utility.Collections
{
    /// <summary>
    /// List style class that fires events when items are added / deleted.
    /// </summary>
    public class EventingList<T> : IList<T>
    {
        #region vars
        private List<T> _items = new List<T>();
        private bool _allowsDup = true;
        #endregion

        #region events
        public event EventHandler<EventInfo<T>> PreDataChanged;
        public event EventHandler<EventInfo<T>> DataChanged;
        #endregion

        #region properties
        public T[] ToArray()
        {
            return _items.ToArray();
        }

        protected List<T> UnderlyingList
        {
            get
            {
                return _items;
            }
        }

        public int Count
        {
            get
            {
                return _items.Count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        public T this[int index]
        {
            get
            {
                return _items[index];
            }
            set
            {
                if (!_items[index].Equals(value))
                {
                    T itemToRemove = _items[index];

                    OnPreDataChanged(new EventInfo<T>(EventType.Deleted, itemToRemove));
                    OnPreDataChanged(new EventInfo<T>(EventType.Added, value));

                    _items[index] = value;

                    OnDataChanged(new EventInfo<T>(EventType.Deleted, itemToRemove));
                    OnDataChanged(new EventInfo<T>(EventType.Added, value));
                }
            }
        }

        public bool AllowsDuplicates
        {
            get
            {
                return _allowsDup;
            }
            set
            {
                _allowsDup = value;
            }
        }
        #endregion

        #region Event handlers
        protected virtual void OnPreDataChanged(EventInfo<T> eventInfo)
        {
            if (PreDataChanged != null)
            {
                PreDataChanged(this, eventInfo);
            }
        }

        protected virtual void OnDataChanged(EventInfo<T> eventInfo)
        {
            if (DataChanged != null)
            {
                DataChanged(this, eventInfo);
            }
        }
        #endregion

        #region IList<T> Members

        public int IndexOf(T item)
        {
            return _items.IndexOf(item);
        }


        public void Insert(int index, T item)
        {
            if (!AllowsDuplicates && _items.Contains(item)) return;
            OnPreDataChanged(new EventInfo<T>(EventType.Added, item));
            _items.Insert(index, item);
            OnDataChanged(new EventInfo<T>(EventType.Added, item));
        }

        public void RemoveAt(int index)
        {
            OnPreDataChanged(new EventInfo<T>(EventType.Deleted, _items[index]));
            _items.RemoveAt(index);
            OnDataChanged(new EventInfo<T>(EventType.Deleted, _items[index]));
        }

        public void MoveItem(int from, int to)
        {
            if (from < 0 || from >= _items.Count)
            {
                throw new ArgumentOutOfRangeException("from");
            }
            if (to < 0 || to >= _items.Count)
            {
                throw new ArgumentOutOfRangeException("to");
            }

            T temp = _items[from];
            _items.RemoveAt(from);
            _items.Insert(to, temp);
        }

        

        #endregion

        #region ICollection<T> Members

        public void Add(T item)
        {
            if (!AllowsDuplicates && _items.Contains(item)) return;
            OnPreDataChanged(new EventInfo<T>(EventType.Added, item));
            _items.Add(item);
            OnDataChanged(new EventInfo<T>(EventType.Added, item));
        }

        public void AddRange(IEnumerable<T> items)
        {
            List<T> copy = new List<T>(items);
            if (!AllowsDuplicates)
            {
                foreach (T t in items)
                {
                    if (copy.Contains(t))
                    {
                        copy.Remove(t);
                    }
                }
            }

            OnPreDataChanged(new EventInfo<T>(EventType.Added, items));
            _items.AddRange(copy);
            OnDataChanged(new EventInfo<T>(EventType.Added, items));
        }

        public virtual void Clear()
        {
            if (_items.Count > 0)
            {
                OnPreDataChanged(new EventInfo<T>(EventType.Deleted, _items));
                _items.Clear();
                OnDataChanged(new EventInfo<T>(EventType.Deleted, _items));
            }
        }

        public bool Contains(T item)
        {
            return _items.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            _items.CopyTo(array, arrayIndex);
        }

        public void RemoveRange(int start, int count)
        {
            if (_items.Count == 0) return;
            List<T> items = _items.GetRange(start, count);
            OnPreDataChanged(new EventInfo<T>(EventType.Deleted, items));
            _items.RemoveRange(start, count);
            OnDataChanged(new EventInfo<T>(EventType.Deleted, items));
        }

        public void RemoveRange(IEnumerable<T> items)
        {
            OnPreDataChanged(new EventInfo<T>(EventType.Deleted, items));
            foreach (T t in items) Remove(t);
            OnDataChanged(new EventInfo<T>(EventType.Deleted, items));
        }

        public bool Remove(T item)
        {
            OnPreDataChanged(new EventInfo<T>(EventType.Deleted, item));
            _items.Remove(item);
            OnDataChanged(new EventInfo<T>(EventType.Deleted, item));
            return false;
        }

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            return _items.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _items.GetEnumerator();
        }

        #endregion        
    }
}